home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / net3d-0.08 / view.c < prev    next >
C/C++ Source or Header  |  1995-06-22  |  30KB  |  1,160 lines

  1. /* view.c
  2.  *
  3.  * Functions for converting 3-d to 2-d, among other things.
  4.  */
  5.  
  6. #include "net3d.h"
  7. #include "icons.h"
  8. #include "view_icon.h"
  9.  
  10. /* Stipple used for shaded objects. */
  11. #define stipple_width 2
  12. #define stipple_height 2
  13. static char stipple_bits[] = {
  14.    0x01, 0x02};
  15.  
  16. /* Pixmaps for icons */
  17. static Pixmap wall[14];
  18.  
  19. /* Global variables used for graphics */
  20. Display *display;
  21. Window view_win;
  22. Pixmap view_pm;
  23. GC view_gc;
  24. unsigned long black,white;
  25. Colormap view_cm;
  26. bool wireframe=0;
  27.  
  28. int window_w=WINDOW_W;
  29. int window_h=WINDOW_H;
  30.  
  31. static struct object *mergesort(struct object *, int);
  32.  
  33.  
  34. /* information about map cache efficiency */
  35. int mappointsdone=0;
  36. int mapcachehits=0;
  37.  
  38. static struct point **mapcache;
  39. static XPoint **mapcachep;
  40. static int **mapcupd;
  41.  
  42. void init3d(struct map *mp, struct view *vw)
  43. {
  44. int i;
  45.  
  46. /* Create the map cache array, which contains the view-
  47.  * converted points of the map vertices.
  48.  */
  49. mapcache = (struct point **)calloc(mp->map_w+1,sizeof(struct point *));
  50. for(i=0; i<=mp->map_w; i++)
  51.     mapcache[i] = (struct point *)calloc(mp->map_h+1,sizeof(struct point));
  52.  
  53. /* Create the map cache perspective array, which contains the
  54.  * perspective divided points for the map polygons.
  55.  */
  56. mapcachep = (XPoint **)calloc(mp->map_w+1,sizeof(XPoint *));
  57. for(i=0; i<=mp->map_w; i++)
  58.     mapcachep[i] = (XPoint *)calloc(mp->map_h+1,sizeof(XPoint));
  59.  
  60. /* Create the map cache update array, which determines if a value
  61.  * in the map cache is up to date.
  62.  */
  63. mapcupd = (int **)calloc(mp->map_w+1,sizeof(int *));
  64. for(i=0; i<=mp->map_w; i++)
  65.     mapcupd[i] = (int *)calloc(mp->map_h+1,sizeof(int));
  66. }
  67.  
  68. /* For each object, check if it is non-moving, allowing time to be
  69.  * saved later on.
  70.  */
  71. void initmightsaves(struct object *opos)
  72. {
  73. for(; opos; opos=opos->next) {
  74.     /* mightsave is  true if some time can be saved on this object, 
  75.      * as it's world co-ords position doesn't change.
  76.      */
  77.     opos->mightsave = (!opos->parent || opos->parent->type == t_scenery ||
  78.                 opos->parent->type == t_static || opos->parent->type 
  79.             == t_mine);
  80.     }
  81. }
  82.  
  83. /* worldtoview -
  84.  *
  85.  * vw        - view co-ordinate system to convert to
  86.  * ohead    - the object list
  87.  * drive    - vehicle the player is driving
  88.  * vmode    - current viewing mode
  89.  */
  90. void worldtoview(struct view *vw, struct object *ohead, struct
  91.  vehicle *drive, int vmode)
  92. {
  93. float orth;
  94. int i;
  95. register struct object *opos;
  96. struct point tmp;
  97. int change=0;
  98. struct point vo;
  99. static int call=0;
  100. bool mightsave;
  101. float vwx,vwy,vwz;
  102.  
  103. /* increase the counter of the number of calls to worldtoview(). This
  104.  * is used to check the validity of values in the map cache.
  105.  */
  106. call++;
  107.  
  108. /* force v to be orthogonal to n */
  109. orth = dotprod(&vw->v,&vw->n)/dotprod(&vw->n,&vw->n);
  110. vw->v.x -= orth*vw->n.x;
  111. vw->v.y -= orth*vw->n.y;
  112. vw->v.z -= orth*vw->n.z;
  113.  
  114. /* create u = v x n */
  115. vw->u.x = vw->v.y*vw->n.z - vw->v.z*vw->n.y;
  116. vw->u.y = vw->v.z*vw->n.x - vw->v.x*vw->n.z;
  117. vw->u.z = vw->v.x*vw->n.y - vw->v.y*vw->n.x;
  118.  
  119. /* normalise u,v and n */
  120. normalise(&vw->u);
  121. normalise(&vw->v);
  122. normalise(&vw->n);
  123.  
  124. /* create view matrix */
  125. vw->vmat[0][0]=vw->u.x;
  126. vw->vmat[0][1]=vw->u.y;
  127. vw->vmat[0][2]=vw->u.z;
  128.  
  129. vw->vmat[1][0]=vw->v.x;
  130. vw->vmat[1][1]=vw->v.y;
  131. vw->vmat[1][2]=vw->v.z;
  132.  
  133. vw->vmat[2][0]=vw->n.x;
  134. vw->vmat[2][1]=vw->n.y;
  135. vw->vmat[2][2]=vw->n.z;
  136.  
  137. /* check for changes */
  138. if (chksum(vw->u) != chksum(vw->last.u)) {
  139.     vw->last.u = vw->u;
  140.     change |= ch_u;
  141.     }
  142. if (chksum(vw->v) != chksum(vw->last.v)) {
  143.     vw->last.v = vw->v;
  144.     change |= ch_v;
  145.     }
  146. if (chksum(vw->n) != chksum(vw->last.n)) {
  147.     vw->last.n = vw->n;
  148.     change |= ch_n;
  149.     }
  150. if (chksum(vw->vrp) != chksum(vw->last.vrp)) {
  151.     /* don't store old vrp till later, since it might be
  152.      * needed in calculating an offset for linear motion.
  153.      */
  154.     change |= ch_vrp;
  155.     }
  156.  
  157. /* check for a change in the view-mode */
  158. if (vmode != vw->last.vmode) {
  159.     change |= ch_all;
  160.     vw->last.vmode = vmode;
  161.     }
  162.  
  163. /* Store the change from this call, to be used by depthsort.
  164.  */
  165. vw->change = change;
  166.  
  167. /* pre-calc offset from old vrp. However, this change is in world
  168.  * co-ordinates, and must be converted to view to be useful.
  169.  */
  170. tmp.x = vw->vrp.x - vw->last.vrp.x;
  171. tmp.y = vw->vrp.y - vw->last.vrp.y;
  172. tmp.z = vw->vrp.z - vw->last.vrp.z;
  173. mmult(&tmp,vw->vmat,&vo);
  174.  
  175. for(opos=ohead; opos; opos=opos->next) {
  176.     mightsave = opos->mightsave && opos->cvalid;
  177.  
  178.     /* pre-calc position of object wrt view */
  179.     vwx = opos->pos.x - vw->vrp.x;
  180.     vwy = opos->pos.y - vw->vrp.y;
  181.     vwz = opos->pos.z - vw->vrp.z;
  182.  
  183.     for(i=0; i<opos->pcount; i++) {
  184.         int x,y;
  185.         register float pers,perx,pery;
  186.         float z;
  187.  
  188.         if (!opos->parent) {
  189.             /* This is a ground polygon. Check the map cache
  190.              * to see if this point has already been calculated
  191.              * and perspective divided.
  192.              */
  193.             mappointsdone++;
  194.             x = opos->mx + (i%2);
  195.             y = opos->my + (i>>1);
  196.             if (mapcupd[x][y] == call) {
  197.                 /* This point has already been done! */
  198.                 mapcachehits++;
  199.                 opos->cpoints[i] = mapcache[x][y];
  200.                 opos->ppoints[i] = mapcachep[x][y];
  201.                 continue;
  202.                 }
  203.             }
  204.  
  205.         if (mightsave) {
  206.             /* Some time might be saved, as this object
  207.              * cannot move.
  208.              */
  209.             if ((change & ch_uvn) && !(change & ch_vrp)) {
  210.                 /* only u,v or n has changed, vrp is
  211.                  * constant.
  212.                  */
  213.                 tmp.x = opos->points[i].x + vwx;
  214.                 tmp.y = opos->points[i].y + vwy;
  215.                 tmp.z = opos->points[i].z + vwz;
  216.                 if (change & ch_u || change & ch_n) {
  217.                     opos->cpoints[i].x =
  218.                      cmult(&tmp,vw->vmat[0]);
  219.                     opos->cpoints[i].z =
  220.                      cmult(&tmp,vw->vmat[2]);
  221.                     }
  222.                 if (change & ch_v)
  223.                      opos->cpoints[i].y =
  224.                      cmult(&tmp,vw->vmat[1]);
  225.                 }
  226.             else if ((change & ch_vrp) && !(change & ch_uvn)) {
  227.                 /* only the vrp has changed, u,v & n
  228.                  * are constant. This means that the
  229.                  * new positions of the points are
  230.                  * linearly offset from the old.
  231.                  */
  232.                 opos->cpoints[i].x -= vo.x;
  233.                 opos->cpoints[i].y -= vo.y;
  234.                 opos->cpoints[i].z -= vo.z;
  235.                 }
  236.             else if (!(change & ch_vrp) && !(change & ch_uvn)) {
  237.                 /* no change in vrp or u,v and n. Do
  238.                  * nothing.
  239.                  */
  240.                 }
  241.             else {
  242.                 /* Both u,v or n and the vrp have changed.
  243.                  * Do a total re-calc.
  244.                  */
  245.                 tmp.x = opos->points[i].x + vwx;    
  246.                 tmp.y = opos->points[i].y + vwy;    
  247.                 tmp.z = opos->points[i].z + vwz;    
  248.                 mmult(&tmp,vw->vmat,opos->cpoints+i);
  249.                 }
  250.             }
  251.         else {
  252.             /* This object moves. No chance of saving
  253.              * time.
  254.              */
  255.             tmp.x = opos->points[i].x + vwx;    
  256.             tmp.y = opos->points[i].y + vwy;    
  257.             tmp.z = opos->points[i].z + vwz;    
  258.             mmult(&tmp,vw->vmat,opos->cpoints+i);
  259.             }
  260.  
  261.         /* do perspective division for this point */
  262.         z = opos->cpoints[i].z;
  263.         if (z < 0) {
  264.             /* If the point is behind the viewer, don't bother
  265.              * to do proper perspective division.
  266.              */
  267.             perx = opos->cpoints[i].x*SCALE + window_w/2;
  268.             pery = -opos->cpoints[i].y*SCALE + window_h/2;
  269.             }
  270.         else {
  271.             pers = 1.0 + z/vw->d;
  272.             perx = (opos->cpoints[i].x/pers)*SCALE + window_w/2;
  273.             pery = -(opos->cpoints[i].y/pers)*SCALE + window_h/2;
  274.             }
  275.         /* allow for overflowing the 16-bit numbers in an XPoint */
  276.         if (perx > 32767.0)
  277.             opos->ppoints[i].x = 32767;
  278.         else if (perx < -32768.0)
  279.             opos->ppoints[i].x = -32768;
  280.         else
  281.             opos->ppoints[i].x = perx;
  282.         if (pery > 32767.0)
  283.             opos->ppoints[i].y = 32767;
  284.         else if (pery < -32768.0)
  285.             opos->ppoints[i].y = -32768;
  286.         else
  287.             opos->ppoints[i].y = pery;
  288.  
  289.         if (!opos->parent) {
  290.             /* This is a ground polygon that has not
  291.              * been taken from the cache. Store it in
  292.              * and mark it as updated.
  293.              */
  294.             mapcache[x][y] = opos->cpoints[i];
  295.             mapcachep[x][y] = opos->ppoints[i];
  296.             mapcupd[x][y] = call;
  297.             }
  298.         }
  299.  
  300.     /* At least one conversion from world to view has been totally
  301.      * done. Mark cpoints as valid.
  302.      */
  303.     opos->cvalid = true;
  304.     }
  305. /* store old vrp. */
  306. vw->last.vrp = vw->vrp;
  307. }
  308.  
  309.  
  310. /* render - draws the current 3-d view into the view window
  311.  *
  312.  * vw        - current viewing position and direction
  313.  * ohead    - head of the object list
  314.  * drive    - vehicle currently being controlled by player
  315.  * vmode    - inside, outside, lookup, etc.. view
  316.  * mp        - terrain map
  317.  */
  318. void render(struct view *vw, struct object *ohead, struct vehicle
  319.  *drive, int vmode, struct map *mp)
  320. {
  321. struct object *opos;            /* position in object list */
  322. int i,j;
  323. XPoint plotx[MAX_POINTS_PER_FACE];
  324. struct point tmp;
  325. long ax,ay,bx,by;            /* for backface removal */
  326. long p0x,p0y,p1x,p1y,p2x,p2y;
  327. int rej;
  328. float pers;                /* for perspective division */
  329. struct polygon *poly;
  330. int *vtmp;
  331. int pc;
  332. int offleft, offright, offtop, offbottom;
  333. struct point horizon;
  334. int reallydrawn=0;
  335.  
  336. /* minimum and maximum points of the box around the vehicle the player
  337.  * has a lock on.
  338.  */
  339. int lxmin,lymin;
  340. int lxmax,lymax;
  341. bool drawlockbox=false;
  342.  
  343. lxmin=SHRT_MAX; lymin=SHRT_MAX;
  344. lxmax=SHRT_MIN;    lymax=SHRT_MIN;
  345.  
  346. XSetForeground(display,view_gc,mp->skycol);
  347. XFillRectangle(display,view_pm,view_gc,0,0,window_w,window_h);
  348.  
  349. if (mp->stars)
  350.     drawstars(mp,vw);
  351.  
  352. /* The ground is drawn as a large, shaded rectangle instead of a
  353.  * real object in 3d.
  354.  */
  355. if (mp->ground) {
  356.     if (!vw->n.x && !vw->n.y && vw->n.z < 0) {
  357.         /* Allow for the situation where the player is looking
  358.          * straight down onto the ground.
  359.          */
  360.         XSetForeground(display,view_gc,mp->gcol*32+26);
  361.         XFillRectangle(display,view_pm,view_gc,0,0,window_w,window_h);
  362.         }
  363.     else if (!vw->n.x && !vw->n.y && vw->n.z > 0) {
  364.         /* Or where the player is looking straight up into the
  365.          * sky.
  366.          */
  367.         }
  368.     else {
  369.         horizon.x=vw->vrp.x + vw->n.x*1000000;
  370.         horizon.y=vw->vrp.y + vw->n.y*1000000;
  371.         horizon.z=0.0;
  372.         mmult(&horizon,vw->vmat,&tmp);
  373.         pers = 1.0 + tmp.z/vw->d;
  374.         tmp.y = -(tmp.y/pers)*SCALE + window_h/2;
  375.         if (wireframe) {
  376.             XSetForeground(display,view_gc,mp->gcol*32+26);
  377.             XDrawLine(display,view_pm,view_gc,0,tmp.y,window_w,
  378.                   tmp.y);
  379.             }
  380.         else {
  381. #if SHADEDGROUND
  382.             int i;
  383.             float strip;
  384.             int shade;
  385.     
  386.             strip=(window_h-tmp.y)/32.0;
  387.             for(i=0; i<32; i++) {
  388.                 /* adjust shade for height above ground */
  389.                 shade=i-32*(vw->vrp.z/VIEW_RANGE);
  390.                 XSetForeground(display,view_gc,shade<0 ?
  391.                            mp->gcol*32 : mp->gcol*32+shade);
  392.                 XFillRectangle(display,view_pm,view_gc,0,
  393.                            tmp.y+strip*i,window_w,strip+1);
  394.                 }
  395. #else
  396.             XSetForeground(display,view_gc,95);
  397.             XFillRectangle(display,view_pm,view_gc,0,tmp.y,
  398.                        window_w,window_h-tmp.y);
  399. #endif
  400.             }
  401.         }
  402.     }
  403.  
  404. for(opos=ohead; opos; opos=opos->next) {
  405.     /* don't draw the vehicle if inside it */
  406.     if (drive && opos->parent == drive && (vmode==4 || vmode==6 ||
  407.      vmode == 8)) {
  408.         continue;
  409.         }
  410.     /* don't draw missile if inside it */
  411.     if (drive && opos->parent && opos->parent->vid == drive->missile &&
  412.      vmode==8) {
  413.         continue;
  414.         }
  415.  
  416.     /* don't draw objects that are too far away, or totally behind
  417.      * the viewer.
  418.      */
  419.     if (/* opos->cdist > VIEW_RANGE || */ opos->dist <= 0.0)
  420.         continue;
  421.  
  422.  
  423.     /* check for visibility, and if so draw each face */
  424.     for(i=0; i < opos->fcount; i++) {
  425.         /* quick reference to the point list for this
  426.          * object.
  427.          */
  428.         register XPoint *currx;
  429.  
  430.         currx = opos->ppoints;
  431.         rej=0;
  432.         poly=opos->faces+i;
  433.         vtmp=poly->vertices;
  434.         if (poly->type == f_face) {
  435.             /* changed to use points after perspective
  436.              * division, because perspective changes
  437.              * what can be seen.
  438.              */
  439.             p0x=currx[vtmp[0]].x; p0y=currx[vtmp[0]].y;
  440.             p1x=currx[vtmp[1]].x; p1y=currx[vtmp[1]].y;
  441.             p2x=currx[vtmp[2]].x; p2y=currx[vtmp[2]].y;
  442.             ax=p0x-p1x; ay=p0y-p1y;
  443.             bx=p2x-p1x; by=p2y-p1y;
  444.             if (opos->clockwise) {
  445.                 if (ax*by <= ay*bx)
  446.                     continue;
  447.                 }
  448.             else {
  449.                 if (ax*by >= ay*bx)
  450.                     continue;
  451.                 }
  452.             }
  453.  
  454.         /* store points for face in XPoint array */
  455.         pc=poly->pcount;
  456.         for(j=0;j < pc;j++)
  457.             plotx[j]=currx[vtmp[j]];
  458.         plotx[pc]=currx[vtmp[0]];
  459.  
  460.         /* check if polygon is totally outside window.
  461.          * for this to be true, all points must be off to one
  462.          * side. */
  463.         offleft=offright=offtop=offbottom=0;
  464.         for(j=0;j < pc;j++) {
  465.             if (plotx[j].x < 0)
  466.                 offleft++;
  467.             else if (plotx[j].x > window_w)
  468.                 offright++;
  469.             if (plotx[j].y < 0)
  470.                 offbottom++;
  471.             else if (plotx[j].y > window_h)
  472.                 offtop++;
  473.             }
  474.         if (offleft==pc || offright==pc || offtop==pc || offbottom==pc)
  475.             rej=1;
  476.  
  477.         if (!rej) {
  478.             unsigned long colbase, colshade;
  479.  
  480.             /* adjust colour for distance from view */
  481.             if (poly->type != f_glass) {
  482.                 colshade=poly->colour%32;
  483.                 colbase=poly->colour-colshade;
  484.                 if (poly->colour >= 32) {
  485.                     if (opos->cdist > VIEW_RANGE)
  486.                         colshade=0;
  487.                     else if (opos->cdist < 0)
  488.                         ;
  489.                     else
  490.                         colshade -= (colshade*opos->
  491.                          cdist/VIEW_RANGE);
  492.                     }
  493.                 }
  494.  
  495.             /* draw face, using correct polygon style */
  496.             XSetForeground(display,view_gc,colbase+colshade);
  497.             if (wireframe) {
  498.                 /* draw only outlines of polygons
  499.                  * and spheres.
  500.                  */
  501.                 switch(poly->type) {
  502.                 case f_sphere: {
  503.                     float rad;
  504.                     float pers;
  505.                     float z;
  506.  
  507.                     /* calculate on-screen radius
  508.                      * of sphere.
  509.                      */
  510.                     z = opos->cpoints[poly->vertices[0]].z;
  511.                     pers = 1.0 + z/vw->d;
  512.                     rad = (poly->radius/pers)*SCALE;
  513.  
  514.                     if (rad < 1) {
  515.                         XDrawPoint(display,view_pm,
  516.                          view_gc,plotx[0].x,
  517.                          plotx[0].y);
  518.                         }
  519.                     else {
  520.                         XDrawArc(display,view_pm,
  521.                          view_gc,plotx[0].x-rad,
  522.                          plotx[0].y-rad,rad*2,rad*2,
  523.                          0,360*64);
  524.                         }
  525.                     break;
  526.                     }
  527.                 case f_point:
  528.                     XDrawPoint(display,view_pm,view_gc,
  529.                      plotx[0].x,plotx[0].y);
  530.                     break;
  531.                 default:
  532.                     XDrawLines(display,view_pm,view_gc,
  533.                      plotx,pc+1,CoordModeOrigin);
  534.                     break;
  535.                     }
  536.                 }
  537.             else {
  538.                 /* draw filled polygons correctly */
  539.                 switch(poly->type) {
  540.                 case f_face:
  541.                 case f_plane:
  542.                     XFillPolygon(display,view_pm,view_gc,
  543.                      plotx,pc+1,Convex,CoordModeOrigin);
  544.                     break;
  545.                 case f_line:
  546.                     XDrawLines(display,view_pm,view_gc,
  547.                      plotx,pc,CoordModeOrigin);
  548.                     break;
  549.                 case f_wireframe:
  550.                     XDrawLines(display,view_pm,view_gc,
  551.                      plotx,pc+1,CoordModeOrigin);
  552.                     break;
  553.                 case f_glass:
  554.                     XSetPlaneMask(display,view_gc,224L);
  555.                     XSetForeground(display,view_gc,
  556.                      poly->colour<<5);
  557.                     XFillPolygon(display,view_pm,view_gc,
  558.                      plotx,pc+1,Convex,CoordModeOrigin);
  559.                     XSetPlaneMask(display,view_gc,255L); 
  560.                     break;
  561.                 case f_sphere: {
  562.                     float rad;
  563.                     float pers;
  564.                     float z;
  565.  
  566.                     /* calculate on-screen radius
  567.                      * of sphere.
  568.                      */
  569.                     z = opos->cpoints[poly->vertices[0]].z;
  570.                     pers = 1.0 + z/vw->d;
  571.                     rad = (poly->radius/pers)*SCALE;
  572.  
  573.                     if (rad < 1) {
  574.                         XDrawPoint(display,view_pm,
  575.                          view_gc,plotx[0].x,
  576.                          plotx[0].y);
  577.                         }
  578.                     else {
  579.                         XFillArc(display,view_pm,
  580.                          view_gc,plotx[0].x-rad,
  581.                          plotx[0].y-rad,rad*2,rad*2,
  582.                          0,360*64);
  583.                         }
  584.                     break;
  585.                     }
  586.                 case f_point:
  587.                     XDrawPoint(display,view_pm,view_gc,
  588.                      plotx[0].x,plotx[0].y);
  589.                     break;
  590.                 case f_shaded:
  591.                     XSetFillStyle(display,view_gc,
  592.                      FillStippled);
  593.                     XFillPolygon(display,view_pm,view_gc,
  594.                      plotx,pc+1,Convex,CoordModeOrigin);
  595.                     XSetFillStyle(display,view_gc,
  596.                      FillSolid);
  597.                     break;
  598.                 default:
  599.                     printf("unknown face type???\n");
  600.                     }
  601.                 }
  602.  
  603.             /* if there is a lock on this object, adjust the
  604.              * lock bounding box.
  605.              */
  606.             if (opos->parent && drive &&
  607.              opos->parent->vid == drive->lock) {
  608.                 int i;
  609.                 for(i=0; i<pc; i++) {
  610.                     if (plotx[i].x < lxmin)
  611.                         lxmin=plotx[i].x;
  612.                     else if (plotx[i].x > lxmax)
  613.                         lxmax=plotx[i].x;
  614.                     if (plotx[i].y < lymin)
  615.                         lymin=plotx[i].y;
  616.                     else if (plotx[i].y > lymax)
  617.                         lymax=plotx[i].y;
  618.                     drawlockbox=true;
  619.                     }
  620.                 }
  621.             reallydrawn++;
  622.  
  623. #if DEBUG
  624.             for(j=0;j < pc;j++)
  625.                 printf("%d,%d ",plotx[j].x,plotx[j].y);
  626.             putchar('\n');
  627. #endif
  628.             }
  629.         }
  630.     }
  631. /* draw the lock bounding box, if the vehicle is in sight */
  632. if (drawlockbox) {
  633.     XSetForeground(display,view_gc,159);
  634.     XDrawRectangle(display,view_pm,view_gc,lxmin,lymin,
  635.      lxmax-lxmin,lymax-lymin);
  636.     }
  637. }
  638.  
  639.  
  640. /* depthsort - bubble sorts the list of objects, from furthest to
  641.  * closest.
  642.  *
  643.  * ohead    - the list of objects
  644.  * vw        - contains viewer's position
  645.  */
  646. struct object *depthsort(struct object *ohead, struct view *vw)
  647. {
  648. int sort;                /* still sorting ? */
  649. struct object *opos,**oprev,*temp;    /* position in list */
  650. int scount=0;
  651. int ocount = 0;                /* total number of objects */
  652.  
  653. if (!ohead)
  654.     return NULL;
  655.  
  656. /* distance to a face is the distance to it's furthest point,
  657.  * along the Z axis.
  658.  */
  659. for(opos=ohead; opos; opos=opos->next) {
  660.     float mxdist = -VIEW_RANGE*VIEW_RANGE*2;
  661.     float midist = VIEW_RANGE*VIEW_RANGE*2;
  662.     int i;
  663.  
  664.     for(i=0; i<opos->pcount; i++) {
  665.         float dist;
  666.  
  667.         dist = opos->cpoints[i].z;
  668.         if (dist > mxdist)
  669.             mxdist = dist;
  670.         if (dist < midist)
  671.             midist = dist;
  672.         }
  673.     opos->dist = mxdist;
  674.     opos->cdist = midist;
  675.     ocount++;
  676.     }
  677.  
  678. /* If the view has been rotated, then a large number of depths will have
  679.  * changed and mergesort should be used.
  680.  * If not, then the distances to objects will be largely unchanged and
  681.  * little sorting will be needed, making bubble sort an easy choice :)
  682.  */
  683. if (vw->change & ch_uvn) {
  684.     ohead = mergesort(ohead,ocount);
  685.     }
  686. else {
  687.     do {
  688.         sort=0;
  689.         for(opos=ohead,oprev=&ohead; opos->next; opos=opos->next) {
  690.             if (opos->dist < opos->next->dist) {
  691.                 (*oprev)=opos->next;
  692.                 temp=opos->next;
  693.                 opos->next=temp->next;
  694.                 temp->next=opos;
  695.                 opos=temp;
  696.                 sort=1;
  697.                 scount++;
  698.                 }
  699.             oprev=&((*oprev)->next);
  700.             }
  701.         } while(sort);
  702.     }
  703.  
  704. return(ohead);
  705. }
  706.  
  707.  
  708. /* mergesort - merge sorts a list of objects.
  709.  *
  710.  * oh        - the head of the object list
  711.  * oc        - total objects in the list
  712.  */
  713. static struct object *mergesort(struct object *oh, int oc)
  714. {
  715. if (oc <= 1) {
  716.     /* Already sorted (of course!) */
  717.     return oh;
  718.     }
  719. else {
  720.     int halfway;
  721.     int i;
  722.     struct object **op;    /* pointer to pointer to halfway node */
  723.     struct object *bk;    /* pointer to halfway node */
  724.     struct object *l1, *l2;    /* sorted halves */
  725.     struct object *slist;    /* final sorted list */
  726.     struct object **sl;
  727.  
  728.     /* Recursively call mergesort again on the two halves of the list */
  729.     halfway = oc/2;
  730.     for(op=&oh, i=0; i < halfway; op = &((*op)->next))
  731.         i++;
  732.     bk = *op;
  733.     *op = NULL;
  734.     l1 = mergesort(oh,halfway);
  735.     l2 = mergesort(bk, oc - halfway);
  736.  
  737.     /* rejoin the two halves */
  738.     sl = &slist;
  739.     while(l1 || l2) {
  740.         if (l1 != NULL && (l2 == NULL || l1->dist > l2->dist)) {
  741.             *sl = l1;
  742.             sl = &(l1->next);
  743.             l1 = l1->next;
  744.             }
  745.         else {
  746.             *sl = l2;
  747.             sl = &(l2->next);
  748.             l2 = l2->next;
  749.             }
  750.         }
  751.     *sl = NULL;
  752.     return slist;
  753.     }
  754. }
  755.  
  756. void initX(struct map *mp, int argc, char **argv)
  757. {
  758. int screen_num;
  759. Screen *screen_ptr;
  760. static XSizeHints size_hints;
  761. Visual *default_visual;
  762. XColor ncol;
  763. int i;
  764. XEvent event;
  765. XGCValues gcvals;
  766. Colormap def_cm;
  767. Pixmap view_icon;
  768.  
  769. /* Open connection to the server, and get some useful Xlib values. */
  770. if (!(display=XOpenDisplay(NULL))) {
  771.     char *dname;
  772.  
  773.     dname = XDisplayName(NULL);
  774.     printf("Error opening display %s\n",dname ? dname : "");
  775.     exit(1);
  776.     }
  777. screen_num=DefaultScreen(display);
  778. screen_ptr=DefaultScreenOfDisplay(display);
  779. black=BlackPixel(display,screen_num);
  780. white=WhitePixel(display,screen_num);
  781.  
  782. /* Create the game window */
  783. view_win=XCreateSimpleWindow(display,RootWindow(display,screen_num),100,100,
  784.  WINDOW_W,WINDOW_H,1,black,white);
  785.  
  786. /* Create the icon pixmap */
  787. view_icon = XCreateBitmapFromData(display,view_win,view_icon_bits,
  788.         view_icon_width,view_icon_height);
  789.  
  790. /* Set window properties and input mask */
  791. size_hints.flags=PMinSize;
  792. size_hints.min_width=315;
  793. size_hints.min_height=200;
  794. XSetStandardProperties(display,view_win,"net3d "NET3D_VERSION,"net3d",
  795.                view_icon,argv,argc,&size_hints);
  796. XSelectInput(display,view_win,StructureNotifyMask | KeyPressMask |
  797.  ButtonPressMask);
  798.  
  799. XMapWindow(display,view_win);
  800.  
  801. /* create the pixmap for double buffering */
  802. view_pm=XCreatePixmap(display,view_win,WINDOW_W,WINDOW_H,8);
  803.  
  804. /* create the GC used in the game */
  805. view_gc=XCreateGC(display,RootWindow(display,screen_num),0,0);
  806. XSetForeground(display,view_gc,white);
  807. XFillRectangle(display,view_pm,view_gc,0,0,window_w,window_h);
  808.  
  809. /* create the stipple pixmap and set the GC to use it, as well as
  810.  * cancelling graphics_exposures on the GC.
  811.  */
  812. gcvals.stipple=XCreateBitmapFromData(display,view_win,stipple_bits,
  813.  stipple_width,stipple_height);
  814. gcvals.graphics_exposures=false;
  815. XChangeGC(display,view_gc,GCGraphicsExposures | GCStipple,&gcvals);
  816.  
  817. default_visual=DefaultVisual(display,screen_num);
  818. view_cm=XCreateColormap(display,view_win,default_visual,AllocAll);
  819. def_cm = DefaultColormap(display,screen_num);
  820. /* allocate custom colourmap
  821.  * 0-31     = unchanged
  822.  * 32-63    = grey shades
  823.  * 64-95    = green shades
  824.  * 96-127    = blue shades
  825.  * 128-159    = red shades
  826.  * 160-191    = brown shades
  827.  * 192-223    = lilacs (55, 13, 47)
  828.  * 224-255    = cyans (0, 63, 63)
  829.  */
  830. ncol.flags = DoRed | DoGreen | DoBlue;
  831. for(i=0; i < 32; i++) {
  832.     /* First 32 colours are from WM */
  833.     ncol.pixel=i;
  834.     XQueryColor(display,def_cm,&ncol);
  835.         XStoreColor(display,view_cm,&ncol);
  836.         /* greys */
  837.         ncol.pixel=i+32;
  838.     fadecolour(mp->rfade,mp->gfade,mp->bfade,65535,65535,65535,i,&ncol);
  839.         XStoreColor(display,view_cm,&ncol);
  840.         /* greens */
  841.         ncol.pixel=i+64;
  842.     fadecolour(mp->rfade,mp->gfade,mp->bfade,0,65535,0,i,&ncol);
  843.         XStoreColor(display,view_cm,&ncol);
  844.         /* blues */
  845.         ncol.pixel=i+96;
  846.     fadecolour(mp->rfade,mp->gfade,mp->bfade,0,0,65535,i,&ncol);
  847.         XStoreColor(display,view_cm,&ncol);
  848.         /* reds */
  849.         ncol.pixel=i+128;
  850.         ncol.red=i<<11; ncol.green=0; ncol.blue=0;
  851.     fadecolour(mp->rfade,mp->gfade,mp->bfade,65535,0,0,i,&ncol);
  852.         XStoreColor(display,view_cm,&ncol);
  853.         /* browns */
  854.         ncol.pixel=i+160;
  855.         ncol.red=i<<11;
  856.         ncol.green=i < 4 ? 0 : (i-4)<<11;
  857.         ncol.blue=i < 8 ? 0 : (i-8)<<11;
  858.     fadecolour(mp->rfade,mp->gfade,mp->bfade,65535,57343,49151,i,&ncol);
  859.         XStoreColor(display,view_cm,&ncol);
  860.         /* lilacs */
  861.         ncol.pixel=i+192;
  862.         ncol.red=i<<11;
  863.         ncol.green=i<<10;
  864.         ncol.blue=i<<11;
  865.     fadecolour(mp->rfade,mp->gfade,mp->bfade,65535,32767,65535,i,&ncol);
  866.         XStoreColor(display,view_cm,&ncol);
  867.         /* cyans */
  868.         ncol.pixel=i+224;
  869.         ncol.red=0;
  870.         ncol.green=i<<11;
  871.         ncol.blue=i<<11;
  872.     fadecolour(mp->rfade,mp->gfade,mp->bfade,0,65535,65535,i,&ncol);
  873.         XStoreColor(display,view_cm,&ncol);
  874.     }
  875. XSetWindowColormap(display,view_win,view_cm);
  876. XFlush(display);
  877.  
  878. /* wait for the window to appear */
  879. do {
  880.     XNextEvent(display,&event);
  881.     } while(event.type != 21);
  882. XInstallColormap(display,view_cm);
  883. XFlush(display);
  884. printf("starting game\n");
  885. }
  886.  
  887. long sqr(long x)
  888. {
  889. return x*x;
  890. }
  891.  
  892. /* change colours 25-31 to a random reddish-yellow colours */
  893. void cyclefire(double tm)
  894. {
  895. XColor ncol;
  896. int i;
  897.  
  898. /* srand((((int)tm)*1000)%65536); */
  899. for(i=25; i<32; i++) {
  900.     ncol.pixel=i;
  901.     ncol.red=65535;
  902.     ncol.green=rand()%65536;
  903.     ncol.blue=0;
  904.     ncol.flags = DoRed | DoGreen | DoBlue;
  905.     XStoreColor(display,view_cm,&ncol);
  906.     }
  907. }
  908.  
  909. void drawextras(struct vehicle *drive, bool winner)
  910. {
  911. if (!drive) {
  912.     /* If there is no vehicle to drive, therefore the player
  913.      * must be dead. Tell them so :)
  914.      */
  915.     XSetForeground(display,view_gc,63);
  916.     XDrawString(display,view_pm,view_gc,10,10,DEATH_MSG,
  917.      strlen(DEATH_MSG));
  918.     }
  919. if (drive && winner) {
  920.     /* someone has won the game, and since this client is still
  921.      * alive, it must be us.
  922.      */
  923.     XSetForeground(display,view_gc,63);
  924.     XDrawString(display,view_pm,view_gc,10,30,WINNER_MSG,
  925.      strlen(WINNER_MSG));
  926.     }
  927. /* copy the completed pixmap to the front */
  928. XCopyArea(display,view_pm,view_win,view_gc,0,0,window_w,window_h,0,0);
  929. XFlush(display);
  930. XSync(display,False);
  931. }
  932.  
  933. /* draw the player's radar, gunsight, hit points, ammo, resources and lock.
  934.  */
  935. void drawradar(struct vehicle *vhead, struct vehicle *drive)
  936. {
  937. struct vehicle *v;            /* vehicle up to in list */
  938. float xd,yd,radsq;            /* position of vehicle on map */
  939. float x,y;                /* position to draw the dot */
  940. long typecols[]={191,95,85,45,52,255,159,32,159,52,255,85,45,159};
  941. char hpbuf[20],ammobuf[20],resbuf[20];    /* strings for displaying things */
  942.  
  943. /* don't draw anything if vehicle has been destroyed */
  944. if (!drive)
  945.     return;
  946.  
  947. /* draw radar circle */
  948. XSetForeground(display,view_gc,32);
  949. XFillArc(display,view_pm,view_gc,window_w-60,0,60,60,0,360*64);
  950.  
  951. for(v=vhead; v; v=v->next) {
  952.     if (v->type == t_scenery)
  953.         continue;
  954.     xd = v->parts[0]->cpoints[0].x;
  955.     yd = v->parts[0]->cpoints[0].z;
  956.     radsq = xd*xd + yd*yd;
  957.     /* check if vehicle is in range */
  958.     if (radsq < RADAR_RANGE*RADAR_RANGE) {
  959.         /* Plot the vehicle at the correct position.
  960.          */
  961.         x = window_w-30 + (xd/RADAR_RANGE)*30;
  962.         y = 30 - (yd/RADAR_RANGE)*30;
  963.         XSetForeground(display,view_gc,typecols[v->type]);
  964.         XDrawPoint(display,view_pm,view_gc,x,y);
  965.         }
  966.     }
  967.  
  968. /* draw an outline around the radar */
  969. XSetForeground(display,view_gc,150);
  970. XDrawArc(display,view_pm,view_gc,window_w-60,0,60,60,0,360*64);
  971.  
  972. /* draw a gunsight */
  973. XSetForeground(display,view_gc,63);
  974. XDrawLine(display,view_pm,view_gc,window_w/2-10,window_h/2,window_w/2+10,
  975.  window_h/2);
  976. XDrawLine(display,view_pm,view_gc,window_w/2,window_h/2-10,window_w/2,
  977.  window_h/2+10);
  978.  
  979. /* display hit points */
  980. XSetForeground(display,view_gc,159);
  981. sprintf(hpbuf,"HP %d",drive->hp);
  982. XDrawString(display,view_pm,view_gc,window_w-60,75,hpbuf,strlen(hpbuf));
  983.  
  984. /* display ammo */
  985. XSetForeground(display,view_gc,159);
  986. sprintf(ammobuf,"AMMO %d",drive->ammo);
  987. XDrawString(display,view_pm,view_gc,window_w-60,90,ammobuf,strlen(ammobuf));
  988.  
  989. /* display resources */
  990. XSetForeground(display,view_gc,90);
  991. sprintf(resbuf,"RES %d",drive->res);
  992. XDrawString(display,view_pm,view_gc,window_w-60,105,resbuf,strlen(resbuf));
  993.  
  994. /* draw name info about vehicle lock is on */
  995. XSetForeground(display,view_gc,159);
  996. for(v=vhead; v && v->vid != drive->lock; v=v->next)
  997.     ;
  998. if (v) {
  999.     /* lock is valid. Draw the vehicle name, and name of the player
  1000.      * who owns it, if any.
  1001.      */
  1002.     XDrawString(display,view_pm,view_gc,10,50,v->name,strlen(v->name));
  1003.     if ((v->owner == o_player || v->owner == o_network) && v->pnum != -1) {
  1004.         XDrawString(display,view_pm,view_gc,10,65,pnames[v->pnum],
  1005.          strlen(pnames[v->pnum]));
  1006.         }
  1007.     }
  1008. else {
  1009.     /* cancel lock */
  1010.     drive->lock = -1;
  1011.     }
  1012. }
  1013.  
  1014. /* display the velocity, hit points and altitute of the player's
  1015.  * vehicle.
  1016.  */
  1017. void drawinfo(struct vehicle *drive)
  1018. {
  1019. int vline;                /* length of velocity line */
  1020. int aline;                /* length of altitude line */
  1021.  
  1022. if (!drive)
  1023.     return;
  1024.  
  1025. /* draw velocity bar */
  1026. XSetForeground(display,view_gc,32);
  1027. XFillRectangle(display,view_pm,view_gc,5,window_h-35,151,16);
  1028. XSetForeground(display,view_gc,125);
  1029. XDrawRectangle(display,view_pm,view_gc,5,window_h-35,151,16);
  1030. if (drive->max.velocity) {
  1031.     if (drive->velocity > 0)
  1032.         XSetForeground(display,view_gc,90);
  1033.     else
  1034.         XSetForeground(display,view_gc,145);
  1035.     vline=148*(dabs(drive->velocity)/drive->max.velocity);
  1036.     if (vline > 148)
  1037.         vline=148;
  1038.     XFillRectangle(display,view_pm,view_gc,7,window_h-33,vline,13);
  1039.     }
  1040.  
  1041. /* draw altitude bar */
  1042. XSetForeground(display,view_gc,32);
  1043. XFillRectangle(display,view_pm,view_gc,window_w-155,window_h-35,151,16);
  1044. XSetForeground(display,view_gc,125);
  1045. XDrawRectangle(display,view_pm,view_gc,window_w-155,window_h-35,151,16);
  1046. if (drive->max.altitude) {
  1047.     if (drive->parts[0]->pos.z >= 0)
  1048.         XSetForeground(display,view_gc,250);
  1049.     else
  1050.         XSetForeground(display,view_gc,220);
  1051.     aline=148*(dabs(drive->parts[0]->pos.z/drive->max.altitude));
  1052.     if (aline > 148)
  1053.         aline=148;
  1054.     XFillRectangle(display,view_pm,view_gc,window_w-153,window_h-33,
  1055.                aline,13);
  1056.     }
  1057. }
  1058.  
  1059. void drawvmode(int vmode, struct vehicle *drive)
  1060. {
  1061. static char *vwnames[]={"Long Range View",
  1062.             "Lookout View",
  1063.             "External View",
  1064.             "Pilot View",
  1065.             "Satellite View",
  1066.             "Gun View",
  1067.             "Short Range View",
  1068.             "Projectile View",
  1069.             "Interesting Thing View"};
  1070. char vmbuf[100];
  1071.  
  1072. XSetForeground(display,view_gc,127);
  1073. sprintf(vmbuf,"%s : %s",drive->code,vwnames[vmode-1]);
  1074. XDrawString(display,view_pm,view_gc,10,10,vmbuf,strlen(vmbuf));
  1075. }
  1076.  
  1077. void fadecolour(int rst, int gst, int bst, int ren, int gen, int ben,
  1078.  int x, XColor *col)
  1079. {
  1080. col->red = rst + ((ren-rst)*(x/31.0));
  1081. col->green = gst + ((gen-gst)*(x/31.0));
  1082. col->blue = bst + ((ben-bst)*(x/31.0));
  1083. }
  1084.  
  1085. /* Convert all the included icon files into pixmaps.
  1086.  */
  1087. void readicons(void)
  1088. {
  1089. wall[1] = XCreateBitmapFromData(display,view_win,wall1_bits,wall1_width,
  1090.       wall1_height);
  1091. wall[2] = XCreateBitmapFromData(display,view_win,wall2_bits,wall2_width,
  1092.       wall2_height);
  1093. wall[3] = XCreateBitmapFromData(display,view_win,wall3_bits,wall3_width,
  1094.       wall3_height);
  1095. wall[4] = XCreateBitmapFromData(display,view_win,wall4_bits,wall4_width,
  1096.       wall4_height);
  1097. wall[5] = XCreateBitmapFromData(display,view_win,wall5_bits,wall5_width,
  1098.       wall5_height);
  1099. wall[6] = XCreateBitmapFromData(display,view_win,wall6_bits,wall6_width,
  1100.       wall6_height);
  1101. wall[7] = XCreateBitmapFromData(display,view_win,wall7_bits,wall7_width,
  1102.       wall7_height);
  1103. wall[8] = XCreateBitmapFromData(display,view_win,wall8_bits,wall8_width,
  1104.       wall8_height);
  1105. wall[9] = XCreateBitmapFromData(display,view_win,wall9_bits,wall9_width,
  1106.       wall9_height);
  1107. wall[10]= XCreateBitmapFromData(display,view_win,wall10_bits,wall10_width,
  1108.       wall10_height);
  1109. wall[11]= XCreateBitmapFromData(display,view_win,wall11_bits,wall11_width,
  1110.       wall11_height);
  1111. wall[12]= XCreateBitmapFromData(display,view_win,wall12_bits,wall12_width,
  1112.       wall12_height);
  1113. wall[13]= XCreateBitmapFromData(display,view_win,wall13_bits,wall13_width,
  1114.         wall13_height);
  1115. }
  1116.  
  1117. /* Copies the build icons into the window at the bottom. */
  1118. void drawbuildicons(void)
  1119. {
  1120. int i;
  1121. if (buildicons) {
  1122.     /* Blit in the icons */
  1123.     XSetForeground(display,view_gc,32);
  1124.     XSetBackground(display,view_gc,63);
  1125.     for(i=1; i<14; i++)
  1126.         XCopyPlane(display,wall[i],view_pm,view_gc,0,0,wall1_width,
  1127.          wall1_height,(i-1)*wall1_width,window_h - wall1_width,1);
  1128.     }
  1129. }
  1130.  
  1131. void drawstars(struct map *mp, struct view *vw)
  1132. {
  1133. float angle, elev;
  1134. int i;
  1135.  
  1136. angle = atan2(vw->n.x,vw->n.y);
  1137. if (angle < 0)
  1138.     angle += 2*PI;
  1139. elev  = atan2(vw->n.z,sqrt(vw->n.x*vw->n.x + vw->n.y*vw->n.y));
  1140. for(i=0; i<mp->scount; i++) {
  1141.     float ra, re;
  1142.     int x,y;
  1143.  
  1144.     ra = angle - mp->stars[i].bearing;
  1145.     re = elev - mp->stars[i].elevation;
  1146.     x  = window_w/2 + ra*STARSCALE;
  1147.     y  = window_h/2 + re*STARSCALE;
  1148.     XSetForeground(display,view_gc,mp->stars[i].intensity + 32);
  1149.     if (mp->stars[i].radius == 1)
  1150.         XDrawPoint(display,view_pm,view_gc,x,y);
  1151.     else {
  1152.         int r;
  1153.  
  1154.         r = mp->stars[i].radius;
  1155.         XFillArc(display,view_pm,view_gc,x-r,y-r,r*2,r*2,0,360*64);
  1156.         }
  1157.     }
  1158. }
  1159.  
  1160.